package com.ia.aistream.service.dxfcomponent.impl;

import com.alibaba.fastjson.JSONObject;
import com.ia.aistream.manager.common.ICommonFileManager;
import com.ia.aistream.manager.dxfcomponent.conf.DrawConf;
import com.ia.aistream.manager.dxfcomponent.conf.DrawMapConf;
import com.ia.aistream.manager.dxfcomponent.util.DrawExtensionUtil;
import com.ia.aistream.manager.framework.redis.KeyFiled;
import com.ia.aistream.manager.framework.redis.RedisManager;
import com.ia.aistream.manager.framework.util.ShiroUtils;
import com.ia.aistream.mapper.dxfcomponent.DxfComponentMapper;
import com.ia.aistream.mapper.dxfcomponent.DxfDrawMapper;
import com.ia.aistream.model.common.entity.CommonFile;
import com.ia.aistream.model.dxfcomponent.dto.DrawDto;
import com.ia.aistream.model.dxfcomponent.entity.DxfComponent;
import com.ia.aistream.model.dxfcomponent.entity.DxfDraw;
import com.ia.aistream.service.dxfcomponent.DrawAnalysisService;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import java.io.IOException;
import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * @Author xiaoyu
 * @create 2020/12/8 10:51
 */
@Slf4j
@Service
public class DrawAnalysisServiceImpl implements DrawAnalysisService {

    @Autowired
    private ICommonFileManager iCommonFileService;
    @Autowired
    private DxfComponentMapper dxfComponentMapper;
    @Autowired
    private DxfDrawMapper dxfDrawMapper;
    @Autowired
    private RestTemplate restTemplate;
    @Autowired
    private DrawConf drawConf;

    /** 注入文件所在的路径 */
    @Value("${drawConfiguration.fileDir}")

    @Resource(name = "threadPoolTaskExecutor")
    private ThreadPoolTaskExecutor executor;

    @Autowired
    private RedisManager redisManager;

    @Override
    public List<DxfDraw> getDxfDraw(DxfDraw dxfDraw) {
        List<DxfDraw> dxfDrawList = redisManager.hget(KeyFiled.CAD_DWG_INDEX.value, ShiroUtils.getSessionId(), List.class);
        return dxfDrawList;
    }

    /****
     * DWG文件上传，去除杂乱数据， 获取索引信息， 获取设备信息
     * 图片命名采用UUID
     * @param files
     * @return
     * @throws Exception
     */
    @Override
    public synchronized List<DxfDraw> getDrawIndexService(MultipartFile[] files) throws Exception {
        Map<String, Object> result = new HashMap<>();
        // 保存解析后的索引信息
        List<DxfDraw> dxfDrawList = new ArrayList<>();
        List<String> errorFile = new ArrayList<>();
        // 获取Redis中已经存在的数据
        List<DxfDraw> redisList = redisManager.hget(KeyFiled.CAD_DWG_INDEX.value, ShiroUtils.getSessionId(), List.class);
        // 如果数据不为空，则进行数据的累加
        if(redisList != null && redisList.size() > 0){
            // 将之前的数据合并
            dxfDrawList.addAll(redisList);
        }

        // 遍历上传的文件集
        for(MultipartFile file : files){

            // 实现文件上传
            CommonFile commonFile = iCommonFileService.batchUpload(file);

            // 获取上传的文件后缀
            String fileType = commonFile.getFileExt();
            String type = "/" + fileType;


            // 获取服务器上的文件地址
            String filePath = drawConf.getFileDir() + commonFile.getDownloadUrl().substring(commonFile.getDownloadUrl().indexOf(type));
            /*** 如果文件是dwg文件，则调用接口，实现由dwg文件转为dxf文件 **/

            if("dwg".equalsIgnoreCase(fileType)){
                // 调用转化接口
                filePath = dwgToDxfService(filePath);
            }

            // 清洗后的dxf文件根据UUID进行命名
            String dxfFile = filePath.substring(0, filePath.lastIndexOf("/")+1) + filePath.substring(filePath.lastIndexOf("_")+1);

            dxfFile = dxfFile.replace(".dwg", ".dxf");
            dxfFile = dxfFile.replace(".DWG", ".dxf");
            // 获取dxf文件中的索引信息
            Map<String, Object> map = DrawExtensionUtil.getDxfDrawSize(filePath, dxfFile);
            // 如果文件读取失败，则不保存该文件
            if(map==null){
                DxfDraw dxfDraw = new DxfDraw();
                dxfDraw.setFileName(commonFile.getFileName());
                dxfDraw.setFileId(commonFile.getId());
                dxfDraw.setStatus(0);
                dxfDrawList.add(dxfDraw);
                continue;
            }
            DxfDraw dxfDraw = (DxfDraw) map.get("dxfDraw");
            if(dxfDraw.getFileCode()==null && dxfDraw.getFileName()==null){
                System.out.println("文件为空...");
                log.info("文件为空..");
                dxfDraw.setStatus(1);
            }
            double[] size = (double[]) map.get("size");

            // 设置索引信息对应的文件id
            dxfDraw.setFileId(commonFile.getId());

            // 将新提取出的数据保存到集合
            dxfDrawList.add(dxfDraw);

            // 开启设备信息数据上传,并转化为图片
            getDxfComponentService(commonFile.getId(), dxfFile, size, type);
        }


        // 将数据写入Redis数据库
        redisManager.hset(KeyFiled.CAD_DWG_INDEX.value, ShiroUtils.getSessionId(),  dxfDrawList);
        // 将设备的索引信息保存到Redis数据库中,设置过期时间1小时
        redisManager.expire(KeyFiled.CAD_DWG_INDEX.value, 1, TimeUnit.HOURS);

        return dxfDrawList;
    }

    /***
     *
     * 根据文件ID，找到文件在服务器上的地址
     * 将dwg文件转化为JPG文件
     * @param fileId
     * @return
     */
    @Override
    public Map<String, Object> getDrawComponentService(Long fileId) {

        List<DxfDraw> dxfDrawTemporaryList = new ArrayList<>();
       //从Redis数据库中获取设备信息数据
        Map<String,Object> map = redisManager.hget(KeyFiled.CAD_DWG_IMG_COMPONENT.value, String.valueOf(fileId), Map.class);

        List<JSONObject> redisList = redisManager.hget(KeyFiled.CAD_DWG_INDEX.value, ShiroUtils.getSessionId(), List.class);
        
        for (JSONObject ddt : redisList){
            DxfDraw dxf = JSONObject.toJavaObject(ddt, DxfDraw.class);
            if (Objects.equals(fileId,dxf.getFileId())){
                map.put(DrawMapConf.DWG_DXF_INDEX.value,dxf);
                break;
            }
        }
        return map;
    }


    @Transactional
    @Override
    public Integer insertBatchService(DrawDto drawDto) {

        // 获取前台提交的索引信息对象
        DxfDraw dxfDraw = drawDto.getObjIndex();
        // 获取前台提交的设备信息
        List<DxfComponent> componentList = drawDto.getObjEquipment();
        // 获取覆盖还是插入操作
        String flag = drawDto.getFlag();
        /******* 删除数据库中的设备信息和索引信息  ****/
        if("true".equals(flag)){
            //
            DxfDraw dxf = new DxfDraw();
            dxf.setFileCode(dxfDraw.getFileCode());
            dxf.setVersion(dxfDraw.getVersion());
            dxf.setEdition(dxfDraw.getEdition());
            List<DxfDraw> dxfDrawList = dxfDrawMapper.selectDxfDrawList(dxf);
            // 删除索引信息
            dxfDrawMapper.deleteDxfDrawById(dxfDrawList.get(0).getDrawId());
            // 删除设备信息
            dxfComponentMapper.deleteDxfComponentByDrawId(dxfDrawList.get(0).getDrawId());
        }

        /*********************************/
        // 获取当前登录用户的名称
        String userName = ShiroUtils.getLoginName();

        // 封装操作用户数据
        dxfDraw.setCreateBy(userName);
        dxfDraw.setCreateTime(new Date());
        // 将索引信息保存到数据库
        int rows = dxfDrawMapper.insert(dxfDraw);

        if (rows>0){
            /** 给设备信息赋值  */

            if(componentList.size()>0){
                // 遍历设备的集合，给设备信息的其他属性赋值
                for(DxfComponent dxfComponent : componentList){
                    dxfComponent.setDrawId(dxfDraw.getDrawId());
                    dxfComponent.setDrawName(dxfDraw.getFileName());
                    dxfComponent.setCreateBy(userName);
                    dxfComponent.setCreateTime(new Date());
                }
                // 将数据保存到数据库
                int comRows = dxfComponentMapper.insertBatchDxfComponent(componentList);
            }

            // 如果数据存入数据库，则将Redis缓存中的数据 清空
            clearRedisDraw(dxfDraw);
            return rows;
        }
        // 将数据保存到数据库
        return 0;
    }

    @Override
    public String getImgPathService(Long fileId) {
        Map<String,Object> map = redisManager.hget(KeyFiled.CAD_DWG_IMG_COMPONENT.value, String.valueOf(fileId), Map.class);
        String imgPath = (String) map.get(DrawMapConf.DXF_IMAGES.value);
        return imgPath;
    }

    /** 删除redis中的索引信息 **/
    @Override
    public int deleteDrawService(long fileId) {
        // 获取Redis中已经存在的数据
        List<JSONObject> redisList = redisManager.hget(KeyFiled.CAD_DWG_INDEX.value, ShiroUtils.getSessionId(), List.class);

        for (JSONObject ddt : redisList){
            DxfDraw dxf = JSONObject.toJavaObject(ddt, DxfDraw.class);
            if (Objects.equals(fileId,dxf.getFileId())){
                redisList.remove(ddt);
                break;
            }
        }

        // 将数据写入Redis数据库
        redisManager.hset(KeyFiled.CAD_DWG_INDEX.value, ShiroUtils.getSessionId(),  redisList);
        return 1;
    }

    /** 判断图片是否存在 ***/
    @Override
    public boolean checkDxfImg(Long fileId) {
        Map map = redisManager.hget(KeyFiled.CAD_DWG_IMG_COMPONENT.value, String.valueOf(fileId), Map.class);
        return  map.containsKey(DrawMapConf.DXF_IMAGES.value);
    }

    /**
     * 修改Redis中的索引数据
     * @param dxfDraw
     */
    private void clearRedisDraw(DxfDraw dxfDraw){
        List<JSONObject> redisList = redisManager.hget(KeyFiled.CAD_DWG_INDEX.value, ShiroUtils.getSessionId(), List.class);

        for (JSONObject ddt : redisList){
            DxfDraw dxf = JSONObject.toJavaObject(ddt, DxfDraw.class);
            // 通过文件id来区别是否唯一
            if(String.valueOf(dxf.getFileId()).equals(String.valueOf(dxfDraw.getFileId()))){
                redisList.remove(ddt);
                break;
            }
        }

        // 清除Redis中的索引信息
        redisManager.hset(KeyFiled.CAD_DWG_INDEX.value, ShiroUtils.getSessionId(), redisList);

        // 清除redis中的设备信息,将整个key删除
        redisManager.hdel(KeyFiled.CAD_DWG_IMG_COMPONENT.value, String.valueOf(dxfDraw.getFileId()));
    }

    /****
     * 调用接口实现将dwg文件转化为JPG文件
     * @param srcFile
     */
    private void dxfToJpgService(String srcFile, Long key, String fileExt) throws InterruptedException, IOException {
        // 转化为png的图片路径
        String imgName = "";

        String cmd = drawConf.getAcmeCADConverter()
                + " /r /e /ls /p 3 /f 6 /w 297mm /h 210mm /res 400 /a 0 /lw 1 "
                + srcFile; // filePath即旧图片路径，比如D:/testPic.dwg
        Runtime rt = Runtime.getRuntime();
        Process p = rt.exec(cmd);
        p.waitFor();// 等待 进程执行结束
        p.getOutputStream().close();
        p.getInputStream().close();

        imgName = "/profile"+ srcFile.substring(srcFile.indexOf(fileExt));
        imgName = imgName.replace(".dxf", ".png");

        Map map = redisManager.hget(KeyFiled.CAD_DWG_IMG_COMPONENT.value, String.valueOf(key), Map.class);
        map.put(DrawMapConf.DXF_IMAGES.value,imgName);
        redisManager.hset(KeyFiled.CAD_DWG_IMG_COMPONENT.value, String.valueOf(key), map);
    }

    /***
     * 在图纸上传完成后，将解析后的数据。保存到Redis数据库中
     * @param key
     * @param srcFile
     */
    @Async
     void getDxfComponentService(Long key, String srcFile, double[] size, String fileExt){
        executor.execute(new Runnable() {
            @SneakyThrows
            @Override
            public void run() {
                Map<String,Object> map = new HashMap<>();

                // 获取dxf文件中的设备信息
                List<DxfComponent> dxfComponentList = DrawExtensionUtil.getDxfComponent(srcFile, size);
                map.put(DrawMapConf.DXF_COMPONENT_LIST.value,dxfComponentList);
                // 将数据保存到Redis数据库中
                redisManager.
                        hset(KeyFiled.CAD_DWG_IMG_COMPONENT.value, String.valueOf(key), map);
                redisManager.expire(KeyFiled.CAD_DWG_IMG_COMPONENT.value, 1, TimeUnit.HOURS);

                // 处理转化图片
                dxfToJpgService(srcFile, key, fileExt);
            }
        });
    }

    /***
     * 将dwg文件转化为dxf文件
     * @param srcFile
     */
    private String dwgToDxfService(String srcFile) throws IOException, InterruptedException {
        String filePath = srcFile.replace(".dwg", ".dxf");
        filePath = filePath.replace(".DWG", ".dxf");
        String cmd = drawConf.getAcmeCADConverter()
                + " /x2013 " + srcFile + " " + filePath; // filePath即旧图片路径，比如D:/testPic.dwg
        Runtime rt = Runtime.getRuntime();
        Process p = rt.exec(cmd);
        p.waitFor();// 等待 进程执行结束
        return filePath;
    }
}
